#pragma once

namespace LatoolNet {

	using namespace System;

  ///
	///<summary>
	/// This class represents a polynomial.
	///</summary>
	///
	public ref class Polynomial {
	private:
		array<double>^ m_coef;
		double m_offset;
		
		array<double>^ ConvertCoefficients(int dimension, double offset, array<double>^ origcoefs, array<double>^ modcoefs) {
			if (dimension == 1) {
				array<double>^ c1 = gcnew array<double>{-1.0, 1.0};
				modcoefs[0] = origcoefs[0] - origcoefs[1] * offset;
        modcoefs[1] = origcoefs[1];
        return c1;
			} else {
				array<double>^ prevc = ConvertCoefficients(dimension - 1, offset, origcoefs, modcoefs);
				array<double>^ currc = gcnew array<double>(prevc->Length + 1);
				double sign = -1.0;
				currc[currc->Length - 1] = 1.0;
				for (int i = currc->Length - 2; i > 0; i = i - 1) {
					currc[i] = sign * (Math::Abs(prevc[i]) + Math::Abs(prevc[i - 1]));
					sign = -sign;
				}
				currc[0] = sign;
				double accumoffset = 1.0;
				for (int i = currc->Length - 1; i >= 0; i = i - 1) {
					modcoefs[i] += origcoefs[dimension] * currc[i] * accumoffset;
					accumoffset *= offset;
				}
				return currc;
			}
		};

	public:
		Polynomial(array<double>^ coef) {
			m_coef = coef;
			m_offset = 0;
		}

		Polynomial(array<double>^ coef, double offset) {
			m_coef = coef;
			m_offset = offset;
		}

		///
		///<summary>
		/// Returns the coefficients of the polynomial.
		///</summary>
		[Obsolete("This method is deprecated. Please use Coeffcients property instead.")]
		property array<double>^ Coefs {
			array<double>^ get() { return m_coef; }
		};

		///
		///<summary>
		/// Returns the coefficients of the polynomial.
		///</summary>
		property array<double>^ Coeffcients {
			array<double>^ get() { 
				array<double>^ ret = gcnew array<double>(m_coef->Length);
				for (int i = 0; i < ret->Length; i++) {
					ret[i] = 0.0;
				}
				int dimension = m_coef->Length - 1;
				ConvertCoefficients(dimension, m_offset, m_coef, ret);
				return ret;
			}
		};

		///
		///<summary>
		/// Returns the offset of the polynomial .
		///</summary>
		///
		property double Offset {
			double get() { return m_offset; }
		};

		///
		///<summary>
		/// Returns an value for the polynomial.
		///</summary>
		///
		double Value(double x) {
			double ret = 0.0;
			for (int i = 0; i < m_coef->Length; i++) {
				ret = ret + m_coef[i] * Math::Pow((x - m_offset), i);
			}
			return ret;
		};
	};
}